package cn.mooncode.basketballManage.utils;

import cn.mooncode.basketballManage.pojo.CoachActive;
import cn.mooncode.basketballManage.pojo.CoachSimilarity;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: basketballManage
 * @description: 推荐工具类
 * @author: jinweizhi
 * @create: 2021-05-10 20:45
 **/

public class RecommendUtils {

    /**
     * 将教练选择场地封装为map，key为教练ID，value为一个map，包含场地ID以及选择这个场地的次数
     *
     * @param coachActiveList
     * @return
     */
    public static ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, Long>> CoachHits(List<CoachActive> coachActiveList) {
        ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, Long>> activeMap = new ConcurrentHashMap<>();
//        遍历查询到所有的教练点击行为事件
        for (CoachActive coachActive : coachActiveList) {
//            获取教练ID
            Integer cid = coachActive.getCid();
//            获取场地ID
            Integer sid = coachActive.getSid();
//            获取选择次数
            Long hits = coachActive.getHits();

//            判断activeMap中是否存在要添加的教练ID
            if (activeMap.containsKey(cid)) {
//                存在教练ID，更新场地ID与选择次数
                ConcurrentHashMap<Integer, Long> spaceMap = activeMap.get(cid);
                spaceMap.put(sid, hits);
                activeMap.put(cid, spaceMap);
            } else {
//                不存在教练ID
                ConcurrentHashMap<Integer, Long> spaceMap = new ConcurrentHashMap<>();
                spaceMap.put(sid, hits);
                activeMap.put(cid, spaceMap);
            }
        }
        return activeMap;
    }

    /**
     * 计算不同教练之间的相似度，返回对象为不同教练之间的相似度对象
     *
     * @param activeMap
     * @return
     */
    public static List<CoachSimilarity> calcSimilarityWithCoach(ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, Long>> activeMap) {
//        教练之间相似度集合
        List<CoachSimilarity> similarityList = new ArrayList<>();
//        获取所有activeMap中键的集合，也就是所有教练ID
        Set<Integer> coachSet = activeMap.keySet();
        List<Integer> coachList = new ArrayList<>(coachSet);

//        当长度小于2代表只有一个教练的选择行为或者没有选择行为，直接返回
        if (coachList.size() < 2) {
            return similarityList;
        }

//        计算所有用户相似度
        for (int i = 0; i < coachList.size() - 1; i++) {
            for (int j = i + 1; j < coachList.size(); j++) {
//                分别获取两个教练中所有场地的点击量
                ConcurrentHashMap<Integer, Long> spaceHitsMap = activeMap.get(coachList.get(i));
                ConcurrentHashMap<Integer, Long> refSpaceHitsMap = activeMap.get(coachList.get(j));

//                获取场地ID的集合
                Set<Integer> space1Map = spaceHitsMap.keySet();
                Set<Integer> space2Map = refSpaceHitsMap.keySet();
//                获取两个集合的迭代
                Iterator<Integer> iterator1 = space1Map.iterator();
                Iterator<Integer> iterator2 = space2Map.iterator();

                // 两用户之间的相似度
                Double similarity = 0.0;
                // 余弦相似度公式中的分子
                Double molecule = 0.0;
                // 余弦相似度公式中的分母
                Double denominator = 1.0;
                // 余弦相似度公式中分母根号下的两个向量的模的值
                Double vector1 = 0.0;
                Double vector2 = 0.0;

                while (iterator1.hasNext() && iterator2.hasNext()) {
                    Integer sid1 = iterator1.next();
                    Integer sid2 = iterator2.next();
//                    获取点击次数
                    Long hits1 = spaceHitsMap.get(sid1);
                    Long hits2 = refSpaceHitsMap.get(sid2);
//                    累加分子
                    molecule += hits1 * hits2;
//                    累加分母中两个向量的模
                    vector1 += Math.pow(hits1, 2);
                    vector2 += Math.pow(hits2, 2);
                }

//                计算分母
                denominator = Math.sqrt(vector1) * Math.sqrt(vector2);
//                计算整体相似度
                similarity = molecule / denominator;

                CoachSimilarity coachSimilarity = new CoachSimilarity();
                coachSimilarity.setCid(coachList.get(i));
                coachSimilarity.setCid_ref(coachList.get(j));
                coachSimilarity.setSimilarity(similarity);
                similarityList.add(coachSimilarity);
            }
        }
        return similarityList;
    }

    /**
     * 找出与教练行为相似的topN个用户
     *
     * @param cid
     * @param coachSimilarityList
     * @param topN
     * @return
     */
    public static List<Integer> getSimilarityBetweenCoach(Integer cid, List<CoachSimilarity> coachSimilarityList, Integer topN) {
//        保存相似的教练ID
        List<Integer> similarityList = new ArrayList<>();

//        使用堆排序获取相似度最高的前N个教练
        PriorityQueue<CoachSimilarity> minHeap = new PriorityQueue<>(new Comparator<CoachSimilarity>() {
            @Override
            public int compare(CoachSimilarity o1, CoachSimilarity o2) {
                if (o1.getSimilarity() - o2.getSimilarity() > 0) {
                    return 1;
                } else if (o1.getSimilarity() - o2.getSimilarity() == 0) {
                    return 0;
                } else return -1;
            }
        });
        for (CoachSimilarity coachSimilarity : coachSimilarityList) {
            if (minHeap.size() < topN) {
//                当堆中长度小于要找的教练长度时，直接将当前对象存入堆中
                minHeap.offer(coachSimilarity);
            } else if (minHeap.peek().getSimilarity() < coachSimilarity.getSimilarity()) {
//                添加的相似度大于堆中最小的相似度
//                    删除堆顶元素，添加新的相似度对象
                minHeap.poll();
                minHeap.offer(coachSimilarity);
            }
        }

//        取出最大相似度教练
        for (CoachSimilarity coachSimilarity : minHeap) {
            similarityList.add(coachSimilarity.getCid().equals(cid) ? coachSimilarity.getCid_ref() : coachSimilarity.getCid());
        }

        return similarityList;
    }

    /**
     *从相似用户选择场地信息中找到当前教练不经常访问的场地ID
     * @param cid
     * @param similarityList
     * @param coachActiveList
     * @return
     */
    public static List<Integer> getSpaceID(Integer cid, List<Integer> similarityList, List<CoachActive> coachActiveList) {
        List<Integer> recommendSpaceList = new ArrayList<>();
        List<CoachActive> coachActiveList1 = findUsersBrowsBehavior(cid, coachActiveList);

//        对当前教练所选择的场地ID进行排序
        Collections.sort(coachActiveList1, new Comparator<CoachActive>() {
            @Override
            public int compare(CoachActive o1, CoachActive o2) {
                return o1.getSid().compareTo(o2.getSid());
            }
        });

//        遍历所有相似教练，找到推荐场地
        for (Integer refCid : similarityList) {
            List<CoachActive> refCoachActiveList = findUsersBrowsBehavior(refCid, coachActiveList);
            Collections.sort(refCoachActiveList, new Comparator<CoachActive>() {
                @Override
                public int compare(CoachActive o1, CoachActive o2) {
                    return o1.getSid().compareTo(o2.getSid());
                }
            });

//            记录差值最大的场地ID
            Integer maxSid = 0;
//            记录最大差值
            double maxDifferent = 0.0;
            for (int i = 0; i < refCoachActiveList.size(); i++) {
                double different = Math.abs(refCoachActiveList.get(i).getHits() - coachActiveList1.get(i).getHits());
                if (different > maxDifferent) {
                    maxDifferent = different;
                    maxSid = refCoachActiveList.get(i).getSid();
                }
            }
            recommendSpaceList.add(maxSid);
        }
        return recommendSpaceList;
    }

    /**
     * 找到当前用户的浏览行为列表
     * @param cid 当前用户id
     * @param coachActiveList 所有用户的浏览行为列表
     * @return 当前用户的浏览行为列表
     */
    public static List<CoachActive> findUsersBrowsBehavior(Integer cid, List<CoachActive> coachActiveList) {
        List<CoachActive> currActiveList = new ArrayList<>();
        for (CoachActive coachActive : coachActiveList) {
            if (coachActive.getCid().equals(cid)) {
                currActiveList.add(coachActive);
            }
        }
        return currActiveList;
    }

}
